/*
 * Flashlib - QB9 common Flash library
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.qb9.flashlib.net
{
	
	/**
	 * Converts an XML object into a regular (JSON) object.
	 * 
	 * <p>XML Atributes are considered the same as children.</p> 
	 * <p>Nodes with children or attributes are not meant to 
	 * have text content. If found, will be ignored.</p>
	 * <p>The following primitive values are supported:</p>
	 * <ul>
	 * 	<li><code>true</code></li>
	 *  <li><code>false</code></li>
	 *  <li><code>null</code></li>
	 *  <li>Integer and floating numbers</li>
	 *  <li>Hexadecimal numbers, must start with "0x" (case insensitive)</li>
	 * </ul>
	 * <p>If more than one child has the same name, they become an array.</p>
	 * <p>If the childrens' name is the singular version of their parent's
	 * and they all have the same name, then the parent becomes the array.</p>
	 */ 
	
	public final class XMLDeserializer
	{
		public static function toJSON(xml:XML) : Object
		{
			var nodes:Array = children(xml);
			
			// Leaf
			if (nodes.length == 0)
				return parsePrimitive(xml.toString());
				
			var token:Object = {};
			var last:String;
			var many:Boolean = false;

			for each (var node:XML in nodes)
			{
				var name:String = node.name().toString();
				
				if (last && last != name)
					many = true;
				last = name;
				
				var value:Object = toJSON(node);

				// Repeating, convert to array
				if (token[name] != null)
				{
					token[name] = makeArray(token[name]);
					token[name].push(value);
				}
				else
				{
					token[name] = value;
				}
			}
			
			// If all the children have the same name and
			// its name is included in the parent's name...
			if (!many && xml.name().toString().indexOf(name) == 0)
				return makeArray(token[name]); 
			
			return token;
		}
		
		private static function makeArray(obj:Object) : Array
		{
			return obj is Array ? obj as Array : [obj];
		}
		
		private static function children(node:XML) : Array
		{
			var nodes:Array = [];
			for each (var child:XML in node.attributes())
				nodes.push(child);
			for each (child in node.elements())
				nodes.push(child);  
			return nodes;
		}
		
		private static function parsePrimitive(str:String):Object
		{
			// Trim
			str = str.replace(/^\s+|\s+$/g, '');
			
			switch (str)
			{
				case 'true'	: return true;
				case 'false': return false;
				case 'null'	: return null;
				default		:
					// Decimal number (int or float)
					if (/^-?\d+(\.\d+)?$/.test(str))
						return parseFloat(str)
					// Hexadecimal number
					if (/^0x[0-9a-f]+$/i.test(str))
						return parseInt(str,16);

					return str;
			}
		}
		
		public static function fromString(str:String) : Object
		{
			return toJSON(XML(str));
		}
	}
}
